/*
 * TableSorter.java
 *
 * Created on 26 de febrero de 2007, 03:28 PM
 *
 * To change this template, choose Tools | Template Manager
 * and open the template in the editor.
 */



package tablasYBasesdeDatos;

//~--- JDK imports ------------------------------------------------------------

/**
 *
 * @author jose
 */
import java.util.*;

import javax.swing.table.TableModel;
import javax.swing.event.TableModelEvent;

//Imports for picking up mouse events from the JTable. 
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.awt.event.InputEvent;

import javax.swing.JTable;
import javax.swing.table.JTableHeader;
import javax.swing.table.TableColumn;
import javax.swing.table.TableColumnModel;

public class TableSorter extends TableMap {
    int     indexes[];
    Vector  sortingColumns = new Vector();
    boolean ascending      = true;
    int     compares;

    public TableSorter() {
        indexes = new int[0];    // For consistency.
    }

    public TableSorter(TableModel model) {
        setModel(model);
    }

    public void setModel(TableModel model) {
        super.setModel(model);
        reallocateIndexes();
    }

    /*
     *  public int compareRowsByColumn(int row1, int row2, int column)
     * {
     *    Class type = model.getColumnClass(column);
     *    TableModel data = model;
     *
     *    // Check for nulls
     *
     *    Object o1 = data.getValueAt(row1, column);
     *    Object o2 = data.getValueAt(row2, column);
     *
     *    // If both values are null return 0
     *    if (o1 == null && o2 == null) {
     *        return 0;
     *    }
     *    else if (o1 == null) { // Define null less than everything.
     *        return -1;
     *    }
     *    else if (o2 == null) {
     *        return 1;
     *    }
     *
     *  We copy all returned values from the getValue call in case
     * an optimised model is reusing one object to return many values.
     * The Number subclasses in the JDK are immutable and so will not be used in
     * this way but other subclasses of Number might want to do this to save
     * space and avoid unnecessary heap allocation.
     */

    /*
     *   if (type.getSuperclass() == java.lang.Number.class)
     *       {
     *           Number n1 = (Number)data.getValueAt(row1, column);
     *           double d1 = n1.doubleValue();
     *           Number n2 = (Number)data.getValueAt(row2, column);
     *           double d2 = n2.doubleValue();
     *
     *           if (d1 < d2)
     *               return -1;
     *           else if (d1 > d2)
     *               return 1;
     *           else
     *               return 0;
     *       }
     *   else if (type == java.util.Date.class)
     *       {
     *           Date d1 = (Date)data.getValueAt(row1, column);
     *           long n1 = d1.getTime();
     *           Date d2 = (Date)data.getValueAt(row2, column);
     *           long n2 = d2.getTime();
     *
     *           if (n1 < n2)
     *               return -1;
     *           else if (n1 > n2)
     *               return 1;
     *           else return 0;
     *       }
     *   else if (type == String.class)
     *       {
     *           String s1 = (String)data.getValueAt(row1, column);
     *           String s2    = (String)data.getValueAt(row2, column);
     *           int result = s1.compareTo(s2);
     *
     *           if (result < 0)
     *               return -1;
     *           else if (result > 0)
     *               return 1;
     *           else return 0;
     *       }
     *   else if (type == Boolean.class)
     *       {
     *           Boolean bool1 = (Boolean)data.getValueAt(row1, column);
     *           boolean b1 = bool1.booleanValue();
     *           Boolean bool2 = (Boolean)data.getValueAt(row2, column);
     *           boolean b2 = bool2.booleanValue();
     *
     *           if (b1 == b2)
     *               return 0;
     *           else if (b1) // Define false < true
     *               return 1;
     *           else
     *               return -1;
     *       }
     *   else
     *       {
     *           Object v1 = data.getValueAt(row1, column);
     *           String s1 = v1.toString();
     *           Object v2 = data.getValueAt(row2, column);
     *           String s2 = v2.toString();
     *           int result = s1.compareTo(s2);
     *
     *           if (result < 0)
     *               return -1;
     *           else if (result > 0)
     *               return 1;
     *           else return 0;
     *       }
     * }
     */

    /*
     *  public int compare(int row1, int row2)
     * {
     *    compares++;
     *    for(int level = 0; level < sortingColumns.size(); level++)
     *        {
     *            Integer column = (Integer)sortingColumns.elementAt(level);
     *            int result = compareRowsByColumn(row1, row2, column.intValue());
     *            if (result != 0)
     *                return ascending ? result : -result;
     *        }
     *    return 0;
     * }
     */
    public void sort(Object sender) {
        checkModel();
        compares = 0;

        // n2sort();
        // qsort(0, indexes.length-1);
        shuttlesort((int[]) indexes.clone(), indexes, 0, indexes.length);
        System.out.println("Compares: " + compares);
    }

    // This is a home-grown implementation which we have not had time
    // to research - it may perform poorly in some circumstances. It
    // requires twice the space of an in-place algorithm and makes
    // NlogN assigments shuttling the values between the two
    // arrays. The number of compares appears to vary between N-1 and
    // NlogN depending on the initial order but the main reason for
    // using it here is that, unlike qsort, it is stable.
    public void shuttlesort(int from[], int to[], int low, int high) {
        if (high - low < 2) {
            return;
        }

        int middle = (low + high) / 2;

        shuttlesort(to, from, low, middle);
        shuttlesort(to, from, middle, high);

        int p = low;
        int q = middle;
    }

    public void reallocateIndexes() {
        int rowCount = model.getRowCount();

        // Set up a new array of indexes with the right number of elements
        // for the new data model.
        indexes = new int[rowCount];

        // Initialise with the identity mapping.
        for (int row = 0; row < rowCount; row++) {
            indexes[row] = row;
        }
    }

    public void tableChanged(TableModelEvent e) {
        System.out.println("Sorter: tableChanged");
        reallocateIndexes();
        super.tableChanged(e);
    }

    public void checkModel() {
        if (indexes.length != model.getRowCount()) {
            System.err.println("Sorter not informed of a change in model.");
        }
    }

    // The mapping only affects the contents of the data rows.
    // Pass all requests to these rows through the mapping array: "indexes".
    public Object getValueAt(int aRow, int aColumn) {
        checkModel();

        return model.getValueAt(indexes[aRow], aColumn);
    }

    public void setValueAt(Object aValue, int aRow, int aColumn) {
        checkModel();
        model.setValueAt(aValue, indexes[aRow], aColumn);
    }

    public void sortByColumn(int column) {
        sortByColumn(column, true);
    }

    public void sortByColumn(int column, boolean ascending) {
        this.ascending = ascending;
        sortingColumns.removeAllElements();
        sortingColumns.addElement(new Integer(column));
        sort(this);
        super.tableChanged(new TableModelEvent(this));
    }

    // when a column heading is clicked in the JTable.
    public void addMouseListenerToHeaderInTable(JTable table) {
        final TableSorter sorter    = this;
        final JTable      tableView = table;

        tableView.setColumnSelectionAllowed(false);

        MouseAdapter listMouseListener = new MouseAdapter() {
            public void mouseClicked(MouseEvent e) {
                TableColumnModel columnModel = tableView.getColumnModel();
                int              viewColumn  = columnModel.getColumnIndexAtX(e.getX());
                int              column      = tableView.convertColumnIndexToModel(viewColumn);

                if ((e.getClickCount() == 1) && (column != -1)) {
                    System.out.println("Sorting ...");

                    int     shiftPressed = e.getModifiers() & InputEvent.SHIFT_MASK;
                    boolean ascending    = (shiftPressed == 0);

                    sorter.sortByColumn(column, ascending);
                }
            }
        };
        JTableHeader th = tableView.getTableHeader();

        th.addMouseListener(listMouseListener);
    }
}


//~ Formatted by Jindent --- http://www.jindent.com
